#include<linux/fs.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/miscdevice.h>
#include<linux/slab.h>
#include<linux/gfp.h>
#include<linux/uaccess.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xieziao");
MODULE_DESCRIPTION("rw_interface");

/*
	用户态下read系统调用为：
	ssize_t read(int fd, void *buf, size_t count);
	在内核中会调用对应文件实现的函数
	filp是文件的结构体，__user代表buffer是一个用户态地址需要地址翻译，ppos是偏移量
	write也是类似
*/

static ssize_t rw_interface_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
	char *buf;
	int ret;

	//内核输出信息可以用pr_info或者printk
	printk(KERN_WARNING "user read,count: %lu, ppos: %llu", count, *ppos);

	buf = kmalloc(count, GFP_KERNEL | __GFP_ZERO); //kmalloc申请连续的物理内存，而vmalloc并不是
	if (!buf){
		//内核里一定一定要注意内存的申请，如果这里不处理空指针那内核直接就挂了
		//这里是kernel的错误码
		return -ENOMEM;
	}
		
	buf[0] = 0x5a;//赋值看个效果
	buf[count - 1] = 0x5a;
	
	//copy_to_user 关键函数，用户态和内核态之间的数据交换
	ret = copy_to_user(buffer, buf, count);
	//返回0代表成功，返回非0代表复制失败的长度
	if (ret) {
		printk(KERN_WARNING "error! copy_to_user failed when read!\n");
		ret = count - ret;
	} else {
		ret = count;
	}
	*ppos += count; //修改一下偏移
	
	kfree(buf);//释放内存
	return ret;//给用户态的返回值
}

static ssize_t rw_interface_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos)
{
	char *buf;
	int ret;
	printk(KERN_WARNING "user write,count: %lu, ppos: %llu", count, *ppos);

	buf = kmalloc(count, GFP_KERNEL | __GFP_ZERO); 
	if (!buf){
		return -ENOMEM;
	}
	
	//copy_from_user 和 copy_to_user 相反
	ret = copy_from_user(buf, buffer, count);
	if (ret) {
		printk(KERN_WARNING "error! copy_from_user failed when write!\n");
		ret = count - ret; 
	} else {
		ret = count;
	}
	*ppos += ret;
	pr_info("buffer %d\n", buf[0]); //输出一下
	
	kfree(buf);//释放内存
	return ret;
}

static int rw_interface_open(struct inode *inode, struct file *filp)
{
	return 0;
}

//C99初始化结构体语法
static const struct file_operations rw_interface_fops = {
	.owner		= THIS_MODULE,
	.open		= rw_interface_open,
	.read		= rw_interface_read, //对该文件的读都会由该函数处理
	.write		= rw_interface_write,//对该文件的写都会由该函数处理
};

static struct miscdevice rw_interface_dev = {
	MISC_DYNAMIC_MINOR, //用cat /proc/devices 查找一个没被用到的设备号
	"rw_interface",
	&rw_interface_fops,
};

static int __init rw_interface_init(void)
{
	//创建一个misc device的设备文件
	int ret;
    ret = misc_register(&rw_interface_dev);
    return ret;
}

static void __exit rw_interface_exit(void)
{
	misc_deregister(&rw_interface_dev);
}

module_init(rw_interface_init);
module_exit(rw_interface_exit);
